﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

namespace SharePointEnforcer.Utilities
{
    class EnforcerUtils
    {
        public class Files
        {
            /// <summary>
            /// Loads a text file from disk.  This is a central place to load files from as eventually it will include a 
            /// more complex search path 
            /// </summary>
            /// <param name="pFileName">The name of the file to open. Can be a full or relative path</param>
            /// <returns>the content of the file, if available.  Null if not</returns>
            public static string LoadText(string pFileName)
            {
                string result = null;
                string fullName = Find(pFileName);

                if (File.Exists(fullName))
                {
                    try
                    {

                        result = File.ReadAllText(fullName);
                    }
                    catch (Exception ex)
                    {
                        // ~~ log it for informational purposes but do not throw an exception for it.
                    }
                }

                return result;
            }


            /// <summary>
            /// determines whether or not the specified file exists somewhere that it can be loaded
            /// </summary>
            /// <param name="fileName">the name of the file to load</param>
            /// <returns>true if it can, false if not</returns>
            public static bool Exists(string fileName)
            {
                return (Find(fileName) != null);
            }

            /// <summary>
            /// attempts to load the file from wherever it can.  For now, this is pretty simplistic
            /// </summary>
            /// <param name="fileName">the name of the file</param>
            /// <returns>the full path to the file wherever it may reside</returns>
            public static string Find(string fileName)
            {
                if (fileName == null) return null;
                string newName = fileName.Trim();

                if (!File.Exists(newName)) return null;
                return new FileInfo(newName).FullName;
            }
        }


        /// <summary>
        /// Standard Xml Utility methods
        /// </summary>
        public class Xml
        {
            public static string GetAttribute(XmlNode node, string attributeName)
            {
                return GetAttribute(node, attributeName, null);
            }
            public static string GetAttribute(XmlNode node, string attributeName, string defaultValue)
            {
                string result = defaultValue;

                try
                {
                    XmlAttribute attrib = node.Attributes[attributeName];
                    if (attrib != null) result = attrib.InnerText;
                }
                catch
                {
                    /* do nothing - was not able to get the attribute value */
                }

                return result;
            }


            /// <summary>
            /// Returns the content of an XmlNode
            /// </summary>
            /// <param name="pElement">the node to check</param>
            /// <param name="returnTextOnly">this method returns the innerXml by default.  Specifying true will return innerText instead</param>
            /// <returns>the content of the node or null if there is no content</returns>
            public static string GetElementValue(XmlNode pElement, bool returnTextOnly)
            {
                return GetElementValue(pElement, null, returnTextOnly);
            }
            /// <summary>
            /// Returns the content of an XmlNode
            /// </summary>
            /// <param name="pElement">the node to check</param>
            /// <param name="defaultValue">the value to return if the node does not exist or if there is no content</param>
            /// <returns>the content of the node</returns>
            public static string GetElementValue(XmlNode pElement, string defaultValue)
            {
                return GetElementValue(pElement, defaultValue, false);
            }
            /// <summary>
            /// Returns the content of an XmlNode
            /// </summary>
            /// <param name="pElement">the node to check</param>
            /// <param name="defaultValue">the value to return if the node does not exist or if there is no content</param>
            /// <param name="returnTextOnly">this method returns the innerXml by default.  Specifying true will return innerText instead</param>
            /// <returns>the content of the node</returns>
            public static string GetElementValue(XmlNode pElement, string defaultValue, bool returnTextOnly)
            {
                if (pElement == null) return defaultValue;

                if (returnTextOnly)
                {
                    return pElement.InnerText ?? defaultValue;
                }
                else
                {
                    return pElement.InnerXml ?? defaultValue;
                }
            }

            /// <summary>
            /// Loads the specified file into an XmlDocument if possible. 
            /// This loads the file from wherever SPEnforcer can find it instead.  It also includes explicit and hopefully helpful error handling
            /// </summary>
            /// <param name="fileName">the name of the file to load</param>
            /// <returns>The filled XmlDocument container</returns>
            public static XmlDocument LoadXmlDoc(string fileName)
            {
                return LoadXmlDoc(fileName, null);
            }
            public static XmlDocument LoadXmlDoc(string fileName, string content)
            {
                string fileContent = content;
                if (string.IsNullOrEmpty(fileContent))
                {
                    if (!EnforcerUtils.Files.Exists(fileName)) throw new EnforcerException(Resources.Enforcer.err_UtilsXmlFileDoesNotExist, fileName);
                    fileContent = EnforcerUtils.Files.LoadText(fileName);
                }

                if (string.IsNullOrEmpty(fileContent)) throw new EnforcerException(Resources.Enforcer.err_UtilsXmlFileCannotBeEmpty, fileName);

                XmlDocument doc = new XmlDocument();

                try
                {
                    doc.LoadXml(fileContent);
                }
                catch (Exception ex)
                {
                    throw new EnforcerException(ex, Resources.Enforcer.err_MalformedFileXml, fileName, ex.Message);
                }

                return doc;
            }
        }
    }


    class EnforcerException : ApplicationException
    {
        private string _Message;

        public EnforcerException(Exception inner, string message, params object[] args)
            : base(message, inner)
        {
            HandleException(message, args);
        }

        public EnforcerException(string message, params object[] args)
            : base(message)
        {
            HandleException(message, args);
        }

        public EnforcerException(Exception inner)
            : base(inner.Message, inner)
        {
            HandleException(inner);
        }

        private void HandleException(Exception inner)
        {
            HandleException(inner.Message);
        }
        private void HandleException(string message, params object[] args)
        {
            _Message = message;

            try
            {
                if (_Message != null && args.Length > 0)
                {
                    _Message = string.Format(message, args);
                }
            }
            catch(Exception ex)
            {
                /* do nothing - the error message text was not formatted properly for a string.format() call */
                FormattingError = ex;
            }

            if (string.IsNullOrEmpty(message) && base.InnerException != null) message = base.InnerException.Message;
            
            // ~~ Log something somewhere
        }

        public override string Message { get { return _Message; } }

        public Exception FormattingError { get; protected set; }
    }


    class EnforcerStringCollection : List<string>
    {
        public void Add(string pValue, params object[] pArgs)
        {
            if (pArgs.Length > 0)
                base.Add(string.Format(pValue, pArgs));
            else
                base.Add(pValue);
        }
    }

}
